/**
   Project: primeval compiler
   File: SymbolTable.c
   Authors: Sebastian Zanner Matr.Nr.: 0855172
   E-Mail: sebastian.zanner@stud.sbg.ac.at
*/

#include "SymbolTable.h"

/* SymTabStack */
void initSymTabStack()
{
    intType = newType;
    intType-> form = FORMINT;
    intType-> fields = NULL;
    intType-> base = NULL;
    intType-> size = 4;

    boolType = newType;
    boolType-> form = FORMBOOL;
    boolType-> fields = NULL;
    boolType-> base = NULL;
    boolType-> size = 4;

    charType = newType;
    charType-> form = FORMCHAR;
    charType-> fields = NULL;
    charType-> base = NULL;
    charType-> size = 4;

    voidType = newType;
    voidType-> form = FORMVOID;
    voidType-> fields = NULL;
    voidType-> base = NULL;
    voidType-> size = 4;

    unknType = newType;
    unknType-> form = FORMUNKN;
    unknType-> fields = NULL;
    unknType-> base = NULL;
    unknType-> size = 4;

    stringType = newType;
    stringType-> form = FORMARRAY;
    stringType-> fields = NULL;
    stringType-> base = charType;
    stringType-> size = 4;

    bottom = newSymTab;
    initSymTab(bottom);
    top = bottom;
    bottom-> lower = NULL;
    height = 1;
}

void push(symTab_t symTab)
{
    if(symTab neq NULL)
    {
        symTab_t tmp = top;
        top = symTab;
        top-> lower = tmp;
        height = height + 1;
    }
    else
    {
        mark("Can't add a Nullpointer to SymTabStack", 1);
    }
}

void newObj(object_t obj)
{
    addObj(top, obj);
}

object_t search(string_t name)
{
    object_t result = NULL;
    if(top neq NULL)
    {
        symTab_t tmp = top;
        while(tmp neq NULL)
        {
            result = getByName(tmp, name);
            if(result neq NULL and result-> oClass neq CLASSFUNC) //DANGER NO FUNCTIONS WILL BE FOUND
            {
                return result;
            }
            tmp = tmp-> lower;
        }
    }
    else
    {
        mark("Can't find, SymTabStack is empty", 1);
    }
    return NULL;
}

object_t searchProcedure(string_t name)
{
    object_t o = getByName(bottom, name);
    if(o equ NULL or o-> oClass neq CLASSFUNC)
    {
        return NULL;
    }
    return o;
}


void pop()
{
    if(DEBUG)
    {
        printSymTabStack();
    }
    if(bottom neq NULL)
    {
        if(top equ bottom)
        {
            free(bottom);
            top = NULL;
            bottom = NULL;
        }
        else
        {
            symTab_t tmp = top;
            top = top-> lower;
            height = height - 1;
            deleteList(tmp);
        }
    }
    else
    {
        mark("SymTabStack is alread empty", 1);
    }
}

void newStage()
{
    symTab_t subSymTab = newSymTab;
    initSubSymTab(subSymTab);
    push(subSymTab);
}

void printSymTabStack()
{
    symTab_t tmp = top;
    int stage = height;
    while(tmp neq NULL)
    {
        printf("\nStage: %d\n", stage);
        printf("*********\n");
        printSymTable(tmp);
        tmp = tmp-> lower;
        stage = stage - 1;
    }
}

void clearSymTabStack()
{
    while(bottom neq NULL)
    {
        pop();
    }
}

/* Sym Table */
void initSubSymTab(symTab_t symTab)
{
    symTab-> head = NULL;
    symTab-> tail = symTab-> head;
    symTab-> length = 0;
}

void initSymTab(symTab_t symTab)
{
    /* add head element */
    symTab-> head = newObject;
    symTab-> head-> name = "NULL";
    symTab-> head-> oClass = 0;
    symTab-> head-> type = NULL;
    symTab-> head-> val = 0;
    symTab-> head-> scope = SCOPEGLOBAL;
    symTab-> head-> next = NULL;

    symTab-> tail = symTab-> head;
    symTab-> length = 1;


    /* add io functions */
    object_t printInt_o = newObject;
    printInt_o-> name = "printInt";
    printInt_o-> oClass = CLASSFUNC;
    printInt_o-> type = voidType;
    printInt_o-> val = 0;
    printInt_o-> scope = SCOPEGLOBAL;

    object_t param1 = newObject;
    param1-> type = intType;
    param1-> name = "param1";
    symTab_t printIntParams = newSymTab;
    initSubSymTab(printIntParams);
    addObj(printIntParams, param1);
    printInt_o-> params = printIntParams;
    addObj(symTab, printInt_o);
    object_t readInt_o = newObject;
    readInt_o-> name = "readInt";
    readInt_o-> oClass = CLASSFUNC;
    readInt_o-> type = intType;
    readInt_o-> val = 0;
    readInt_o-> scope = SCOPEGLOBAL;
    symTab_t readIntParams = newSymTab;
    initSubSymTab(readIntParams);
    readInt_o-> params = readIntParams;
    addObj(symTab, readInt_o);


    object_t printChar_o = newObject;
    printChar_o-> name = "printChar";
    printChar_o-> oClass = CLASSFUNC;
    printChar_o-> type = voidType;
    printChar_o-> val = 0;
    printChar_o-> scope = SCOPEGLOBAL;
    object_t param2 = newObject;
    param2-> type = charType;
    param2-> name = "param2";
    symTab_t printCharParams = newSymTab;
    initSubSymTab(printCharParams);
    addObj(printCharParams, param2);
    printChar_o-> params = printCharParams;
    addObj(symTab, printChar_o);

    object_t readChar_o = newObject;
    readChar_o-> name = "readChar";
    readChar_o-> oClass = CLASSFUNC;
    readChar_o-> type = charType;
    readChar_o-> val = 0;
    readChar_o-> scope = SCOPEGLOBAL;
    symTab_t readCharParams = newSymTab;
    initSubSymTab(readCharParams);
    readChar_o-> params = readCharParams;
    addObj(symTab, readChar_o);


    object_t printString_o = newObject;
    printString_o-> name = "printString";
    printString_o-> oClass = CLASSFUNC;
    printString_o-> type = voidType;
    printString_o-> val = 0;
    printString_o-> scope = SCOPEGLOBAL;
    object_t param3 = newObject;
    param3-> type = stringType;
    param3-> name = "param3";
    symTab_t printStringParams = newSymTab;
    initSubSymTab(printStringParams);
    addObj(printStringParams, param3);
    printString_o-> params = printStringParams;
    addObj(symTab, printString_o);

}

void add(symTab_t symTab,
         string_t   name,
         int        oClass,
         type_t     type,
         int        val)
{
    /* exists */
    int exists = 0;
    object_t tmp = symTab-> head;
    while(tmp neq symTab-> tail and strcmp(tmp-> name, name) neq 0)
    {
        tmp = tmp-> next;
    }
    if(strcmp(tmp-> name, name) equ 0)
    {
        printf("tmp-> name: %s      name: %s",tmp-> name, name);
        exists = 1;
    }

    if(exists equ 0)
    {
        object_t node = newObject;
        if(node equ NULL)
        {
            mark("No Memory", 1);
            exit(1);
        }
        node-> name = name;
        node-> oClass = oClass;
        node-> type = type;
        node-> val = val;
        node-> next = NULL;

        symTab-> tail-> next = node;
        symTab-> tail = node;

        symTab-> length = symTab-> length + 1;
    }
    else
    {
         mark("Symbol already exists", 1);
    }
}

void addObj(symTab_t symTab,
                object_t node)
{
    if(node neq NULL)
    {
        if(symTab-> head equ NULL)
        {
            symTab-> head = node;
            symTab-> tail = symTab-> head;
            symTab-> length = symTab-> length + 1;
        }
        else
        {
             /* exists */
            int exists = 0;
            object_t tmp = symTab-> head;
            while(tmp neq symTab-> tail and strcmp(tmp-> name, node-> name) neq 0)
            {
                tmp = tmp-> next;
            }
            if(strcmp(tmp-> name, node-> name) equ 0)
            {
                printf("tmp-> name: %s      node-> name: %s\n",tmp-> name, node-> name);
                exists = 1;
            }

            if(exists equ 0)
            {
                node-> next = NULL;

                symTab-> tail-> next = node;
                symTab-> tail = node;

                symTab-> length = symTab-> length + 1;
            }
            else
            {
                mark("Symbol already exists", 1);
            }
        }
    }
    else
    {
        mark("can't add a NullPointer to List", 1);
    }
}

void printSymTable(symTab_t symTab)
{
    if(symTab neq NULL)
    {
        object_t tmp = symTab-> head;
        while(tmp neq NULL)
        {
            printObj(tmp);
            tmp = tmp-> next;
        }
    }
    else
    {
        mark("SymTab not allocated", 1);
    }
}

void deleteByName(symTab_t symTab, string_t name)
{
    object_t tmp = symTab-> head;
    object_t last = symTab-> head;

    while(tmp neq symTab-> tail and strcmp(tmp-> name, name) neq 0)
    {
        last = tmp;
        tmp = tmp->next;
    }

    if(strcmp(tmp-> name, name) equ 0)
    {
        last-> next = tmp-> next;

        if(tmp equ symTab-> head)
        {
            symTab-> head = tmp-> next;
        }
        if(tmp equ symTab-> tail)
        {
            symTab-> tail = last;
        }

        symTab-> length = symTab-> length - 1;
        free(tmp);
    }
    else
    {
        mark("Symbol can't be removed, it does not exist", 1);
    }
}

object_t getByName(symTab_t symTab, string_t name)
{
    if(symTab-> length equ 0)
    {
        return NULL;
    }
    object_t tmp = symTab-> head;
    while(tmp neq symTab-> tail and strcmp(tmp-> name, name) neq 0)
    {
        tmp = tmp-> next;
    }
    if(strcmp(tmp-> name, name) equ 0)
    {
        return tmp;
    }
    return NULL;
}

int calcSize(symTab_t symTab)
{
    if(symTab equ NULL)
    {
        return 4;
    }

    int size = 0;
    object_t tmp = symTab-> head;
    while(1 equ 1)
    {
        size = size + tmp-> type-> size;
        if(tmp neq symTab-> tail)
        {
            tmp = tmp-> next;
        }
        else
        {
            return size;
        }
    }

    return size;
}

int countVariables(symTab_t symTab)
{
    if(symTab equ NULL)
    {
        return 0;
    }

    int count = 0;
    object_t tmp = symTab-> head;
    while(1 equ 1)
    {
        if(tmp-> oClass equ CLASSVAR)
        {
            count = count + 1;
        }
        if(tmp neq symTab-> tail)
        {
            tmp = tmp-> next;
        }
        else
        {
            return count;
        }
    }
    return count;
}

void deleteList(symTab_t symTab)
{
    if(symTab-> head neq NULL)
    {
        object_t tmp = symTab-> head;
        while(tmp neq symTab-> tail)
        {
            object_t killMe;
            killMe = tmp;
            tmp = tmp-> next;
            symTab-> head = tmp;


            //free(killMe-> type);
            free(killMe);
        }
        //free(symTab-> head-> type);
        free(symTab-> head);
        free(symTab);
    }
}

void printObj(object_t object)
{
    if(object neq NULL)
    {
        printf("Name: %s\n", object-> name);
        printf("Class: %d\n", object-> oClass);
        printf("Type: \n");
        if(object-> type neq NULL)
        {
            printType(object-> type);
        }
        else
        {
            printf("    NULL");
        }
        if(object-> params neq NULL)
        {
            printSymTable(object-> params);
        }
        printf("Val: %d\n", object-> val);
        printf("Scope: %d\n\n", object-> scope);
    }
    else
    {
        mark("Object is NULL", 1);
    }
}

/*
void addField(type_t structType, object_t field)
{
    if(structType-> fields equ NULL)
    {
        field-> next = NULL;
        structType-> fields = field;
    }
    else
    {
        object_t cur = structType-> fields;
        int exists = 0;
        while(cur-> next neq NULL)
        {
            if(strcmp(cur-> name, field-> name) equ 0)
            {
                exists = exists + 1;
            }
            cur = cur-> next;
        }
        if(exists equ 0)
        {
            field-> next = NULL;
            cur-> next = field;
        }
        else
        {
            mark("FieldName already in use", 1);
        }
    }

}
*/

void printType(type_t type)
{
    if(type neq NULL)
    {
        printf("    form: %d\n", type-> form);
        printf("    len: %d\n", type-> len);
        printf("    size: %d\n", type-> size);
        if(type-> fields neq NULL)
        {
            printf("    fields: \n");
            printSymTable(type-> fields);
        }
        if(type-> base neq NULL)
        {
            printf("    base: \n");
            printType(type-> base);
        }
    }
    else
    {
        mark("Type is NULL", 1);
    }
}
